package com.ghp.admin.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ghp.admin.mapper.UserMapper;
import com.ghp.admin.service.UserService;
import com.ghp.common.constants.ErrorMessageConstants;
import com.ghp.common.exception.BusinessException;
import com.ghp.common.model.dto.user.UserLoginDTO;
import com.ghp.common.model.dto.user.UserQueryDTO;
import com.ghp.common.model.dto.user.UserRegisterDTO;
import com.ghp.common.model.dto.user.UserUpdateOwnerDTO;
import com.ghp.common.model.entity.User;
import com.ghp.common.model.vo.UserLoginVO;
import com.ghp.common.model.vo.UserVO;
import com.ghp.common.utils.*;
import com.ghp.common.utils.response.ResponseResult;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.ghp.common.constants.RedisConstants.*;
import static com.ghp.common.constants.UserConstants.USER_NAME_PREFIX;
import static com.ghp.common.utils.response.ResponseStatus.*;

/**
 * @author ghp
 * @description 针对表【tb_user(用户表)】的数据库操作Service实现
 * @createDate
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {

    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 用户注册
     *
     * @param userRegisterDTO
     * @return
     */
    @Override
    public ResponseResult userRegister(UserRegisterDTO userRegisterDTO) {
        // 判断两次密码是否一致
        boolean isEqual = userRegisterDTO.getUserPassword().equals(userRegisterDTO.getCheckPassword());
        ThrowUtils.throwIf(!isEqual, CLIENT_USER_NOT_EXIST);
        // 判断验证码是否正确
        String phoneOrEmail = userRegisterDTO.getPhoneOrEmail();
        String captcha = userRegisterDTO.getCaptcha();
        String serverCaptcha = (String) redisTemplate.opsForValue().get(LOGIN_CAPTCHA_KEY + phoneOrEmail);
        if (captcha==null || !captcha.equals(serverCaptcha)){
            return ResponseResult.fail("验证码不正确");
        }
        // 判断账号是否已存在
        User one = this.getOne(new LambdaQueryWrapper<User>()
                .eq(User::getUserAccount, userRegisterDTO.getUserAccount()));
        ThrowUtils.throwIf(Objects.nonNull(one), CLIENT_ACCOUNT_ALREADY_EXIST);
        // 账号不存在可以进行注册
        // 初始化数据: 昵称、头像、AK/SK、密码加密
        String userName = USER_NAME_PREFIX + UserNameGeneratorUtils.generateUsername();
        String userAvatar = "https://img1.imgtp.com/2023/09/05/pHdN5Tju.jpg";
        String accessKey = KeyGeneratorUtils.generateAccessKey();
        String secretKey = KeyGeneratorUtils.generateSecretKey();
        String encryptedPassword = EncryptionUtils.getEncryptedPassword(userRegisterDTO.getUserPassword());
        User user = BeanConvertorUtils.copyBean(userRegisterDTO, User.class);
        user.setUserName(userName);
        user.setUserAvatar(userAvatar);
        user.setAccessKey(accessKey);
        user.setSecretKey(secretKey);
        user.setUserPassword(encryptedPassword);
        // 注册用户
        boolean isSuccess = this.save(user);
        ThrowUtils.throwIf(!isSuccess, SERVICE_INSERT_ERROR);
        return ResponseResult.success();
    }

    /**
     * 用户忘记密码
     *
     * @param userRegisterDTO
     * @return
     */
    @Override
    public ResponseResult userForgetPassword(UserRegisterDTO userRegisterDTO) {
        // 判断两次密码是否一致
        boolean isEqual = userRegisterDTO.getUserPassword().equals(userRegisterDTO.getCheckPassword());
        ThrowUtils.throwIf(!isEqual, CLIENT_USER_NOT_EXIST);
        // 判断验证码是否正确
        String phoneOrEmail = userRegisterDTO.getPhoneOrEmail();
        String captcha = userRegisterDTO.getCaptcha();
        String serverCaptcha = (String) redisTemplate.opsForValue().get(LOGIN_CAPTCHA_KEY + phoneOrEmail);
        if (captcha==null || !captcha.equals(serverCaptcha)){
            return ResponseResult.fail("验证码不正确");
        }
        // 判断当前的 phoneOrEmail 是否与当前账号绑定
        User one = this.getOne(new LambdaQueryWrapper<User>()
                .eq(User::getUserAccount, userRegisterDTO.getUserAccount())
                .eq(User::getPhoneOrEmail, phoneOrEmail));
        ThrowUtils.throwIf(Objects.isNull(one), ErrorMessageConstants.ACCOUNT_NOT_EXISTS);
        // 账号存在则，更新密码
        User user = BeanConvertorUtils.copyBean(userRegisterDTO, User.class);
        user.setId(one.getId());
        boolean isSuccess = this.updateById(user);
        ThrowUtils.throwIf(!isSuccess, SERVICE_UPDATE_ERROR);
        return ResponseResult.success();
    }

    /**
     * 用户登录
     *
     * @param userLoginDTO
     * @return
     */
    @Override
    public ResponseResult<UserLoginVO> userLogin(UserLoginDTO userLoginDTO) {
        String encryptionPassword = EncryptionUtils.getEncryptedPassword(userLoginDTO.getUserPassword());
        // 1、判断用户是否存在
        User user = this.getOne(new LambdaQueryWrapper<User>()
                .eq(User::getUserAccount, userLoginDTO.getUserAccount())
                .eq(User::getUserPassword, encryptionPassword));
        ThrowUtils.throwIf(Objects.isNull(user), CLIENT_USER_NOT_EXIST);
        // 2、用户存在则缓存用户信息，并返回当前用户信息
        UserLoginVO userLoginVO = BeanConvertorUtils.copyBean(user, UserLoginVO.class);
        // 2.1 将当前用户id存入ThreadLocal中
        Long userId = user.getId();
        ThreadLocalUtils.setCurrentId(userId);
        // 2.2 生成token
        String token = JwtGeneratorUtils.createJWT(userId.toString());
        userLoginVO.setToken(token);
        // 2.3 将用户信息存入Redis中
        // 将userLoginVO转成Map，key为字段名 value为字段值，Hash比String更节约空间
        Map<String, Object> userMap = BeanConvertorUtils.beanToMap(userLoginVO);
        String key = LOGIN_USER_KEY + userId;
        redisTemplate.opsForHash().putAll(key, userMap);
        redisTemplate.expire(key, LOGIN_USER_TTL, TimeUnit.SECONDS);
        // 3、返回
        return ResponseResult.success(userLoginVO);
    }

    /**
     * 分页查询用户列表
     *
     * @param userQueryDTO
     * @return
     */
    @Override
    public ResponseResult<Page<User>> listUserByPage(UserQueryDTO userQueryDTO) {
        Page<User> page = new Page<>(userQueryDTO.getCurrentPage(), userQueryDTO.getPageSize());
        this.page(page, getQueryWrapper(userQueryDTO));
        return ResponseResult.success(page);
    }

    /**
     * 分页查询用户列表（用户数据进行了脱敏）
     *
     * @param userQueryDTO
     * @return
     */
    @Override
    public ResponseResult<Page<UserVO>> listUserVOByPage(UserQueryDTO userQueryDTO) {
        ResponseResult<Page<User>> pageResponseResult = this.listUserByPage(userQueryDTO);
        Page<User> userPage = pageResponseResult.getData();
        List<UserVO> userVOList = BeanConvertorUtils.copyBeanList(userPage.getRecords(), UserVO.class);
        Page<UserVO> userVOPage = new Page<>();
        userVOPage.setRecords(userVOList);
        userVOPage.setTotal(userPage.getTotal());
        return ResponseResult.success(userVOPage);
    }

    /**
     * 查询条件
     */
    private LambdaQueryWrapper<User> getQueryWrapper(UserQueryDTO userQueryDTO) {
        Long userId = userQueryDTO.getId();
        String userName = userQueryDTO.getUserName();
        String userRole = userQueryDTO.getUserRole();
        String phoneOrEmail = userQueryDTO.getPhoneOrEmail();
        String sortField = userQueryDTO.getSortField();
        String sortOrder = userQueryDTO.getSortOrder();
        // 构造查询条件
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapperUtils<User>()
                .addSortCondition(sortField, sortOrder)
                .eq(Objects.nonNull(userId), User::getId, userId)
                .eq(StringUtils.isNotBlank(userRole), User::getUserRole, userRole)
                .eq(StringUtils.isNotBlank(phoneOrEmail), User::getPhoneOrEmail, phoneOrEmail)
                .like(StringUtils.isNotBlank(userName), User::getUserName, userName);
        return lambdaQueryWrapper;
    }

    /**
     * 更新个人信息
     *
     * @param userUpdateOwnerDTO
     * @return
     */
    @Override
    public ResponseResult<Boolean> updateOwner(UserUpdateOwnerDTO userUpdateOwnerDTO) {
        Long userId = ThreadLocalUtils.getCurrentId();
        String userName = userUpdateOwnerDTO.getUserName();
        String userAvatar = userUpdateOwnerDTO.getUserAvatar();
        String phoneOrEmail = userUpdateOwnerDTO.getPhoneOrEmail();
        boolean isSuccess = this.update(new LambdaUpdateWrapper<User>()
                .eq(User::getId, userId)
                .set(StringUtils.isNotBlank(userName), User::getUserName, userName)
                .set(StringUtils.isNotBlank(userAvatar), User::getUserAvatar, userAvatar)
                .eq(StringUtils.isNotBlank(phoneOrEmail), User::getPhoneOrEmail, phoneOrEmail));
        ThrowUtils.throwIf(!isSuccess, SERVICE_UPDATE_ERROR);
        return ResponseResult.success();
    }

    /**
     * 获取当前登录用户信息
     *
     * @param userId
     */
    @Override
    public UserLoginVO getUserLoginVO(Long userId) {
        String key = LOGIN_USER_KEY + userId;
        Map<Object, Object> userMap = redisTemplate.opsForHash().entries(key);
        ThrowUtils.throwIf(userMap == null || userMap.isEmpty(), CLIENT_USER_LOGIN_TIMEOUT);
        // 用户存在，则将用户信息保存到ThreadLocal中，方便后续逻辑处理，比如：方便获取和使用用户信息，Redis获取用户信息是具有侵入性的
        UserLoginVO userLoginVO = null;
        try {
            userLoginVO = BeanConvertorUtils.mapToBean(userMap, UserLoginVO.class);
        } catch (Exception e) {
            throw new BusinessException(SYSTEM_ERROR);
        }
        return userLoginVO;
    }

}




